#include <event.h>
#include <event2/bufferevent.h>

#include <eventpp/net_base.h>
#include <eventpp/tcp_handler.h>

#include <eventpp/tcp_connection.h>
using namespace eventpp;

#include <assert.h>

void eventpp::tcp_connection::close()
{
  if ( sock_ != -1 ) {
    evutil_closesocket(sock_);
    sock_ = -1;
  }
  if ( bev_ != NULL ) {
    bufferevent_free( bev_ );
    bev_ = NULL;
  }

	status_ = socked_closed;
}

int tcp_connection::on_accepted( evutil_socket_t sock, sockaddr *sa, int sa_len )
{
	sock_	= sock;
	sa_		= *reinterpret_cast<sockaddr_in*>(sa);

	status_ = socket_connected;

	bev_ = bufferevent_socket_new(base_.base(), sock, BEV_OPT_CLOSE_ON_FREE);
	if( bev_ == NULL )
		return -1;

	bufferevent_enable(bev_, EV_READ|EV_WRITE|EV_PERSIST);
	bufferevent_setcb(bev_, tcp_connection::be_read_cb, tcp_connection::be_write_cb, tcp_connection::be_event_cb, this );
	handler_->on_accepted( this );
	return 0;
}

eventpp::tcp_connection::tcp_connection( net_base & base, tcp_handler * handler )
: base_(base)
, handler_(handler)
{
}

eventpp::tcp_connection::~tcp_connection()
{
	close();
}

int tcp_connection::connect( sockaddr *sa, int sa_len )
{
	sa_ = *reinterpret_cast<sockaddr_in*>(sa);

	bev_ = bufferevent_socket_new(base_.base(), -1, BEV_OPT_CLOSE_ON_FREE);
	if( bev_ == NULL ) {
		return -1;
	}
	bufferevent_setcb(bev_, tcp_connection::be_read_cb, tcp_connection::be_write_cb, tcp_connection::be_event_cb, this );

	if ( bufferevent_socket_connect(bev_, sa, sa_len ) < 0) {
        bufferevent_free(bev_);
		bev_ = NULL;
        return -1;
    }
	status_ = socket_connecting;
}

bool eventpp::tcp_connection::write( const void * data, size_t len )
{
	if ( ! connected() ) {
		return false;
	}

    size_t buffer_size = evbuffer_get_length(get_output());
    if (max_output_ < buffer_size) {
        assert(false);
        return false;
    }
    if (len > max_output_ - buffer_size) {
        return false;
    }
    
	bufferevent_write( bev_, data, len );
    return true;
}

void eventpp::tcp_connection::on_connected()
{
	status_ = socket_connected;

	bufferevent_enable(bev_, EV_READ|EV_WRITE|EV_PERSIST);
	handler_->on_connected( this );
}

size_t eventpp::tcp_connection::read(void * data, size_t len)
{
    if (!connected()) {
        return 0;
    }
    return bufferevent_read(bev_, data, len);
}

void tcp_connection::be_event_cb(struct bufferevent *bev, short what, void *ctx)
{
  printf( "be_event_cb, %x.\n", what );
  tcp_connection * conn = reinterpret_cast<tcp_connection *>(ctx);			
  if ( what & BEV_EVENT_CONNECTED ) {
    conn->on_connected();
  } else if ( (what & BEV_EVENT_READING) && (what & BEV_EVENT_EOF) ){
    conn->handler_->on_closed(conn);
  } else {
    conn->handler_->on_event( what, conn );
  }
}
void tcp_connection::be_read_cb(struct bufferevent *bev, void *ctx)
{
  tcp_connection * conn = reinterpret_cast<tcp_connection *>(ctx);
  conn->handler_->on_read( conn );
}

void tcp_connection::be_write_cb(struct bufferevent *bev, void *ctx)
{
  tcp_connection * conn = reinterpret_cast<tcp_connection *>(ctx);
  conn->handler_->on_write( conn );
}
